package agrep4me;

class Table3 extends Table1 {
	private final static int NBYTES = 256;
	private States[] memo[];
	private States all_alpha;

	private void memoizeSubsets(int i, States[] t, States ex) {
		if (i <= 0)
			return;
		memoizeSubsets(i - 1, t, ex);
		int mask = 1 << i;
		for (int k = mask - 1; k > 0; k--) {
			int index = mask | k;
			t[index] = t[k].copy();
			t[index].or(t[mask]);
		}
	}

	private void memoize(Auto a, States ex) {
		memo = new States[ex.nbytes()][];
		for (int i = 0, base = 0; i < ex.nbytes(); i++, base += 8) {
			States[] t = new States[NBYTES];
			// Ensemble de ze'ro elt
			t[0] = ex.empty();
			// Ensembles d'un elt
			for (int j = 0; j < 8; j++) {
				States s = ex.empty();
				for (Epsilon p = a.epsilon[base + j]; p != null; p = p.next)
					s.add(p.target);
				t[1 << j] = s;
			}
			// Tous les autres
			memoizeSubsets(7, t, ex);
			memo[i] = t;
		}
	}

	private void ferme(States s) {
		int lim = s.nbytes();

		for (int i = 0; i < lim; i++) {
			s.or(memo[i][s.getByte(i)]);
		}
	}

	private void cloture(States ex) {
		States w = ex.empty();
		boolean same;
		do {
			same = true;
			for (int i = 0; i < ex.nbytes(); i++) {
				for (int j = 0; j < NBYTES; j++) {
					w.assign(memo[i][j]);
					ferme(memo[i][j]);
					same = same && w.equals(memo[i][j]);
				}
			}
		} while (!same);
	}

	Table3(Auto a, int nerrs, States ex) {
		super(a, nerrs, ex);
		// System.err.println("Niveau c") ;
		all_alpha = ex.empty();

		for (int i = 0; i <= fstate; i++)
			if (a.trans[i] != null)
				all_alpha.add(i);

		memoize(a, ex);
		cloture(ex);
	}

	States initial() {
		States r = w1.empty();
		r.add(0);
		ferme(r);
		return r;
	}

	States initialErr(States prev) {
		States r = prev.copy();
		r.and(all_alpha);
		r.shift();
		r.or(prev);
		ferme(r);
		return r;
	}

	void step(States s, char c) {
		super.step(s, c);
		ferme(s);
	}

	// pless = previous value with -1 error
	// less = current value with -1 error
	void stepErr(States s, char c, States pless, States less) {
		w1.assign(s);
		w2.assign(pless);
		// Normal
		s.and(alphaGet(c));
		s.shift();
		s.add(0);
		// Introduce errors
		pless.or(less);
		pless.and(all_alpha);
		pless.shift();
		pless.or(w2);
		// Combine
		s.or(pless);
		ferme(s);
		pless.assign(w1);
	}

}
